package cn.kevinking.ai.mcp.provider.utils;

import cn.kevinking.ai.common.constant.McpProviderConstants;
import cn.kevinking.ai.common.model.ToolDefinition;
import cn.kevinking.ai.common.model.ToolParamDefinition;
import cn.kevinking.ai.mcp.provider.annotation.McpTool;
import cn.kevinking.ai.mcp.provider.annotation.McpToolParam;
import cn.kevinking.ai.mcp.provider.configure.SpringContextHolder;
import cn.kevinking.ai.mcp.provider.model.NativeToolDefinition;
import cn.kevinking.ai.mcp.provider.model.tool.McpDictParamConverter;
import cn.kevinking.ai.mcp.provider.properties.McpProviderProperties;
import cn.kevinking.ai.mcp.provider.schema.BfzSchemaGenerator;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import cn.kevinking.ai.mcp.provider.model.NativeToolParameterDefinition;
import org.springframework.beans.BeanUtils;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
import org.springframework.web.util.pattern.PathPattern;
import org.springframework.web.util.pattern.PathPatternParser;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author kevinking
 */
@Slf4j
@UtilityClass
public class McpToolUtils {
	private static final BfzSchemaGenerator SCHEMA_GENERATOR = new BfzSchemaGenerator();
	private static final PathPatternParser PATH_PARSER = new PathPatternParser();

	/**
	 * 扫描@McpTool修饰的method上的参数列表：
	 * @param method 注解修饰目标方法
	 * @return 工具定义对象
	 */
	public List<NativeToolParameterDefinition> from(Method method) {
		// Annotation[i][j]: i代表第几个参数，j代表第i个参数上的第j个注解
		Annotation[][] annotations = method.getParameterAnnotations();
		Parameter[] parameters = method.getParameters();
		final int parameterSize = parameters.length;
		List<NativeToolParameterDefinition> paramDefinitions = new ArrayList<>();
		// 统一映射成一个工具定义对象，参数将会被平铺开
		for (int i = 0; i < parameterSize; i++) {
			if (Arrays.stream(annotations[i]).anyMatch(McpToolParam.class::isInstance)) {
				paramDefinitions.addAll(iterateObjectNode(parameters[i].getType(), parameters[i]));
			}
		}
		if (paramDefinitions.isEmpty()) {
			log.warn("Found no parameters for mcp tool method: {}, failed to register!", method.getName());
		}
		return paramDefinitions;
	}

	public List<NativeToolParameterDefinition> iterateObjectNode(Class<?> targetClazz, Object targetField) {
		List<NativeToolParameterDefinition> definitions = new ArrayList<>();
		ObjectNode root = SCHEMA_GENERATOR.generate(targetClazz);
		if (SCHEMA_GENERATOR.isObjectType(root)) {
			// 如果类型是object，需要继续平铺参数
			List<Field> fields = BfzReflectionUtils.getFieldsWithAnnotation(targetClazz, McpToolParam.class);
			fields.forEach(field -> definitions.addAll(iterateObjectNode(field.getType(), field)));
		} else {
			// 非object类型时为基本类型, JsonSchema工具会解析出type的同时需要解析@McpToolParam配置
			String type = SCHEMA_GENERATOR.getType(root);
			if (StringUtils.hasText(type)) {
				McpToolParam toolParamAnno = null;
				if (targetField instanceof Parameter parameter) {
					toolParamAnno = parameter.getAnnotation(McpToolParam.class);
				} else if (targetField instanceof Field field) {
					toolParamAnno = field.getAnnotation(McpToolParam.class);
				}
				if (toolParamAnno != null) {
					NativeToolParameterDefinition newDef = NativeToolParameterDefinition.fromToolParamAnnotation(toolParamAnno, type);
					definitions.add(newDef);
				}
			}
		}
		return definitions;
	}

	public String generateRouterPath(McpProviderProperties providerProperties, Environment env, String subUrl) {
		String profile = env.getProperty(EnvUtils.SPRING_PROFILE_KEY);
		String routerPath = String.format(
				"/%s/%s%s",
				providerProperties.getName(),
				StringUtils.hasText(profile) ? profile : EnvUtils.SPRING_DEFAULT_PROFILE,
				subUrl
		);
		PathPattern parse = PATH_PARSER.parse(routerPath);
		return parse.getPatternString();
	}

	public String generateToolUrl(Method method) {
		McpTool toolAnno = method.getAnnotation(McpTool.class);
		final String bizScope = StringUtils.hasText(toolAnno.bizScope()) ? toolAnno.bizScope() : McpProviderConstants.DEFAULT_TOOL_BIZ_SCOPE;
		final String toolName = StringUtils.hasText(toolAnno.name()) ? toolAnno.name() : method.getName();
		return String.format("/%s/%s", bizScope, toolName);
	}

	public List<ToolDefinition> toToolDefinitionsFromNative(List<NativeToolDefinition> natives, SpringContextHolder context) {
		return natives.stream().map(ntd -> {
			ToolDefinition td = new ToolDefinition();
			BeanUtils.copyProperties(ntd, td);
			td.setProperties(toToolParamDefinitionsFromNative(ntd.getParamDefinitions(), context));
			return td;
		}).toList();
	}

	private List<ToolParamDefinition> toToolParamDefinitionsFromNative(List<NativeToolParameterDefinition> natives, SpringContextHolder context) {
		return natives.stream().map(ntpd -> {
			ToolParamDefinition tpd = new ToolParamDefinition();
			BeanUtils.copyProperties(ntpd, tpd);
			McpDictParamConverter dictBean = context.getApplicationContext().getBean(ntpd.getDictConverterClazz());
            tpd.setDictEnums(dictBean.convert());
            return tpd;
		}).toList();
	}
}
